package com.leo.boot.feature.merge2;

import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@UtilityClass
public class MergingHandler {

    private static final Map<Class<?>, ClassDelegate> cache = new ConcurrentHashMap<>();

    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static <T extends Mergeable<T>> T merge(T a, T b) {
        Class<T> clazz = (Class<T>) a.getClass();
        T newInstance = clazz.getDeclaredConstructor().newInstance();

        ClassDelegate clazzDelegate = cache.computeIfAbsent(clazz, ClassDelegate::new);
        clazzDelegate.pre(a);
        clazzDelegate.pre(b);
        clazzDelegate.getFieldDelegates().forEach(fieldDelegate -> {
            Object aValue = fieldDelegate.get(a);
            Object bValue = fieldDelegate.get(b);
            aValue = aValue == null ? fieldDelegate.nil() : aValue;
            bValue = bValue == null ? fieldDelegate.nil() : bValue;
            if (fieldDelegate.test(aValue, bValue)) {
                fieldDelegate.set(newInstance, fieldDelegate.op(aValue, bValue));
            }
        });
        clazzDelegate.post(newInstance);

        return newInstance;
    }
}
